1
2
3
4 package edu.jiangxin.apktoolbox.android.i18n;
5
6 import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
7 import edu.jiangxin.apktoolbox.swing.extend.listener.SelectDirectoryListener;
8 import edu.jiangxin.apktoolbox.utils.Constants;
9 import org.apache.commons.collections4.map.HashedMap;
10 import org.apache.commons.io.FileUtils;
11 import org.apache.commons.lang3.StringUtils;
12 import org.apache.commons.lang3.Strings;
13 import org.jdom2.Document;
14 import org.jdom2.Element;
15 import org.jdom2.JDOMException;
16 import org.jdom2.input.SAXBuilder;
17 import org.jdom2.output.Format;
18 import org.jdom2.output.XMLOutputter;
19
20 import javax.swing.*;
21 import java.awt.*;
22 import java.io.*;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.List;
26 import java.util.Map;
27
28
29
30
31
32
33 public class I18nAddPanel extends EasyPanel {
34
35 @Serial
36 private static final long serialVersionUID = 1L;
37
38 private static final String CHARSET = "UTF-8";
39
40 private static final boolean REMOVE_LAST_LF_OPEN = true;
41
42 private static final Map<String, String> replace = new HashedMap<>();
43
44 private JTextField srcTextField;
45
46 private JTextField targetTextField;
47
48 private JTextField itemTextField;
49
50 private int operationCount = 0;
51
52 static {
53 replace.put(""", "jiangxin001");
54 replace.put(" ", "jiangxin002");
55 }
56
57 public I18nAddPanel() throws HeadlessException {
58 super();
59 }
60
61 @Override
62 public void initUI() {
63 BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS);
64 setLayout(boxLayout);
65
66 createSourcePanel();
67 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
68 createTargetPanel();
69 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
70 createItemPanel();
71 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
72 createOperationPanel();
73 }
74
75 private void createOperationPanel() {
76 JPanel operationPanel = new JPanel();
77 operationPanel.setLayout(new BoxLayout(operationPanel, BoxLayout.X_AXIS));
78 add(operationPanel);
79
80 JButton addButton = new JButton(bundle.getString("android.i18n.add.title"));
81 addButton.addActionListener(e -> {
82 String srcPath = checkAndGetDirContent(srcTextField, "android.i18n.add.src.dir", "Source directory is invalid");
83 if (srcPath == null) {
84 return;
85 }
86
87 String targetPath = checkAndGetDirContent(targetTextField, "android.i18n.add.target.dir", "Target directory is invalid");
88 if (targetPath == null) {
89 return;
90 }
91
92 String itemStr = checkAndGetStringContent(itemTextField, "android.i18n.add.items", "Items is empty");
93 if (itemStr == null) {
94 return;
95 }
96
97 List<String> items = new ArrayList<>(Arrays.asList(itemStr.split(";")));
98 operationCount = 0;
99
100 for (String item : items) {
101 int ret = innerProcessor(srcPath, targetPath, item);
102 if (ret != 0) {
103 Toolkit.getDefaultToolkit().beep();
104 JOptionPane.showMessageDialog(this, "Failed, please see the log", "ERROR",
105 JOptionPane.ERROR_MESSAGE);
106 return;
107 }
108 }
109 String message = String.format("Success: items: %d", operationCount);
110 JOptionPane.showMessageDialog(this, message, "INFO", JOptionPane.INFORMATION_MESSAGE);
111 });
112
113 operationPanel.add(addButton);
114 }
115
116 private void createItemPanel() {
117 JPanel itemPanel = new JPanel();
118 itemPanel.setLayout(new BoxLayout(itemPanel, BoxLayout.X_AXIS));
119 add(itemPanel);
120
121 itemTextField = new JTextField();
122 itemTextField.setText(conf.getString("android.i18n.add.items"));
123
124 JLabel itemLabel = new JLabel("Items");
125
126 itemPanel.add(itemTextField);
127 itemPanel.add(Box.createHorizontalGlue());
128 itemPanel.add(itemLabel);
129 }
130
131 private void createTargetPanel() {
132 JPanel targetPanel = new JPanel();
133 targetPanel.setLayout(new BoxLayout(targetPanel, BoxLayout.X_AXIS));
134 add(targetPanel);
135
136 targetTextField = new JTextField();
137 targetTextField.setText(conf.getString("android.i18n.add.target.dir"));
138
139 JButton targetButton = new JButton("Save Directory");
140 targetButton.addActionListener(new SelectDirectoryListener("save to", targetTextField));
141
142 targetPanel.add(targetTextField);
143 targetPanel.add(Box.createHorizontalGlue());
144 targetPanel.add(targetButton);
145 }
146
147 private void createSourcePanel() {
148 JPanel sourcePanel = new JPanel();
149 sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.X_AXIS));
150 add(sourcePanel);
151
152 srcTextField = new JTextField();
153 srcTextField.setText(conf.getString("android.i18n.add.src.dir"));
154
155 JButton srcButton = new JButton("Source Directory");
156 srcButton.addActionListener(new SelectDirectoryListener("select a directory", srcTextField));
157
158 sourcePanel.add(srcTextField);
159 sourcePanel.add(Box.createHorizontalGlue());
160 sourcePanel.add(srcButton);
161 }
162
163 private int innerProcessor(String sourceBaseStr, String targetBaseStr, String itemName) {
164 if (StringUtils.isAnyEmpty(sourceBaseStr, targetBaseStr, itemName)) {
165 logger.error("params are invalid: sourceBaseStr: {}, targetBaseStr: {}, itemName: {}", sourceBaseStr, targetBaseStr, itemName);
166 return -1;
167 }
168 File sourceBaseFile = new File(sourceBaseStr);
169 File targetBaseFile = new File(targetBaseStr);
170 int count = 0;
171
172 File[] sourceParentFiles = sourceBaseFile.listFiles(new FileFilter() {
173 @Override
174 public boolean accept(File pathname) {
175 return pathname.getName().startsWith("values");
176 }
177 });
178 if (sourceParentFiles == null) {
179 logger.error("sourceParentFiles is null");
180 return -1;
181 }
182 for (File sourceParentFile : sourceParentFiles) {
183 File sourceFile = new File(sourceParentFile, "strings.xml");
184
185 Element sourceElement = getSourceElement(sourceFile, itemName);
186 if (sourceElement == null) {
187 logger.warn("sourceElement is null: {}", sourceFile);
188 continue;
189 }
190
191 File targetFile = new File(new File(targetBaseFile, sourceParentFile.getName()), "strings.xml");
192 if (!targetFile.exists()) {
193 logger.warn("targetFile does not exist: {}", sourceFile);
194 continue;
195 }
196 try {
197 preProcess(targetFile);
198 } catch (IOException e) {
199 logger.error("preProcess failed.", e);
200 return -1;
201 }
202 boolean res = setTargetElement(targetFile, sourceElement, itemName);
203 if (!res) {
204 logger.error("setTargetElement failed.");
205 return -1;
206 }
207 try {
208 postProcess(targetFile);
209 } catch (IOException e) {
210 logger.error("postProcess failed.", e);
211 return -1;
212 }
213 logger.info("count: {}, in path: {}, out path: {}", ++count, sourceFile, targetFile);
214 }
215 operationCount += count;
216 logger.info("finish one cycle");
217 return 0;
218 }
219
220 private Element getSourceElement(File sourceFile, String itemName) {
221 if (!sourceFile.exists()) {
222 logger.warn("sourceFile does not exist: {}", sourceFile);
223 return null;
224 }
225 SAXBuilder builder = new SAXBuilder();
226 Document sourceDoc = null;
227 try (InputStream in = new FileInputStream(sourceFile)) {
228 sourceDoc = builder.build(in);
229 logger.info("build source document: {}", sourceFile);
230 } catch (JDOMException | IOException e) {
231 logger.error("build source document failed: {}", sourceFile);
232 return null;
233 }
234 if (sourceDoc == null) {
235 logger.error("sourceDoc is null");
236 return null;
237 }
238 Element sourceElement = null;
239 for (Element sourceChild : sourceDoc.getRootElement().getChildren()) {
240 String sourceValue = sourceChild.getAttributeValue("name");
241 if (sourceValue != null && sourceValue.equals(itemName)) {
242 sourceElement = sourceChild.clone();
243 break;
244 }
245 }
246 return sourceElement;
247 }
248
249 private boolean setTargetElement(File targetFile, Element sourceElement, String itemName) {
250 SAXBuilder builder = new SAXBuilder();
251 Document targetDoc;
252 try {
253 targetDoc = builder.build(targetFile);
254 logger.info("build target document: {}", targetFile);
255 } catch (JDOMException | IOException e) {
256 logger.error("build target document failed: {}", targetFile);
257 return false;
258 }
259 Element targetRoot = targetDoc.getRootElement();
260 boolean isFinished = false;
261 for (Element targetChild : targetRoot.getChildren()) {
262 String targetValue = targetChild.getAttributeValue("name");
263 if (targetValue != null && targetValue.equals(itemName)) {
264 targetChild.setText(sourceElement.getText());
265 isFinished = true;
266 break;
267 }
268 }
269 if (!isFinished) {
270 targetRoot.addContent(" ");
271 targetRoot.addContent(sourceElement);
272 targetRoot.addContent("\n");
273 }
274 XMLOutputter out = new XMLOutputter();
275 Format format = Format.getRawFormat();
276 format.setEncoding("UTF-8");
277 format.setLineSeparator("\n");
278 out.setFormat(format);
279 OutputStream os = null;
280 try {
281 os = new FileOutputStream(targetFile);
282 out.output(targetDoc, os);
283 } catch (IOException e) {
284 logger.error("output fail", e);
285 return false;
286 } finally {
287 if (os != null) {
288 try {
289 os.close();
290 } catch (IOException e) {
291 logger.error("close output stream exception", e);
292 }
293 }
294 }
295 return true;
296 }
297
298 private static void preProcess(File file) throws IOException {
299 String content = FileUtils.readFileToString(file, CHARSET);
300 for (Map.Entry<String, String> entry : replace.entrySet()) {
301 content = content.replaceAll(entry.getKey(), entry.getValue());
302 }
303 FileUtils.writeStringToFile(file, content, CHARSET);
304 }
305
306 private static void postProcess(File file) throws IOException {
307 String content = FileUtils.readFileToString(file, CHARSET);
308 for (Map.Entry<String, String> entry : replace.entrySet()) {
309 content = content.replaceAll(entry.getValue(), entry.getKey());
310 }
311 if (REMOVE_LAST_LF_OPEN) {
312 content = Strings.CS.removeEnd(content, "\n");
313 }
314 FileUtils.writeStringToFile(file, content, CHARSET);
315 }
316
317 }